﻿using Common;
using System.Buffers;
using System.Diagnostics;
using System.IO.Ports;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;

// ReSharper disable InconsistentNaming
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS0169 // Field is never used

namespace NewDevice
{
    /// <summary>
    /// 线程安全的串口通信基类（支持异步操作）
    /// </summary>
    public class SerialBase(string deviceName = "串口总线") : IDisposable
    {
        #region 字段和属性

        private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
        private long _receiveBufferSize;
        private SerialPort? _serialPort;
        private MemoryStream _receiveBuffer = new();
        private bool _disposed;
        private readonly ArrayPool<byte> _arrayPool = ArrayPool<byte>.Shared;

        private readonly object comLock = new object();  // 数据锁，用于线程同步
        private byte[] receivedBytes;  // 接收到的字节数据
        private string receivedString;  // 接收到的字符串数据

        /// <summary>
        /// 设备名称
        /// </summary>
        public string DeviceName { get; set; } = deviceName;

        /// <summary>
        /// 连接状态
        /// </summary>
        public bool IsConnected
        {
            get
            {
                var ret = _serialPort?.IsOpen ?? false;
                return ret;
            }
        }

        #endregion 字段和属性

        #region 连接管理

        /// <summary>
        /// 连接串口（线程安全）
        /// </summary>
        public void Connect(string portName, int baudRate = 9600, int dataBits = 8,
                          StopBits stopBits = StopBits.One, Parity parity = Parity.None)
        {
            _rwLock.EnterWriteLock();
            try
            {
                if (IsConnected) return;

                _serialPort = new SerialPort(portName, baudRate)
                {
                    DataBits = dataBits,
                    StopBits = stopBits,
                    Parity = parity,
                    ReadTimeout = 500,
                    WriteTimeout = 500,
                    Handshake = Handshake.None,
                    ReadBufferSize = 4096,
                    WriteBufferSize = 4096
                };

                //新方法
                _serialPort.DataReceived += OnDataReceived;

                _serialPort.Open();
                _rwLock.ExitWriteLock();
            }
            catch (Exception)
            {
                _rwLock.ExitWriteLock();
                throw;
            }
        }

        /// <summary>
        /// 断开连接（线程安全）
        /// </summary>
        public void Disconnect()
        {
            _rwLock.EnterWriteLock();
            try
            {
                if (_serialPort == null) return;

                _serialPort.DataReceived -= OnDataReceived;
                if (_serialPort.IsOpen)
                {
                    _serialPort.Close();
                }
                _rwLock.ExitWriteLock();
            }
            catch (Exception)
            {
                _rwLock.ExitWriteLock();
                throw;
            }
        }

        #endregion 连接管理

        #region 数据发送

        /// <summary>
        /// 发送ASCII字符串（自动重试）
        /// </summary>
        protected void SendAscii(string message, int retryCount = 3)
        {
            byte[] buffer = _arrayPool.Rent(Encoding.ASCII.GetMaxByteCount(message.Length));
            try
            {
                MyLog.DeviceData(deviceName: DeviceName, sendData: message);
                int bytesWritten = Encoding.ASCII.GetBytes(message, buffer);
                SendBytes(buffer, 0, bytesWritten);
            }
            finally
            {
                _arrayPool.Return(buffer);
            }
        }

        /// <summary>
        /// 发送二进制数据
        /// </summary>
        protected void SendBytes(byte[] data)
        {
            MyLog.DeviceData(deviceName: DeviceName, sendData: BitConverter.ToString(data).Replace("-", " "));

            _rwLock.EnterWriteLock();
            try
            {
                if (_serialPort?.IsOpen != true)
                    throw new InvalidOperationException("串口未连接");

                _serialPort.Write(data, 0, data.Length);
                _rwLock.ExitWriteLock();
            }
            catch (Exception)
            {
                _rwLock.ExitWriteLock();
                throw;
            }
        }

        protected void SendBytes(byte[] data, int startIndex, int length)
        {
            MyLog.DeviceData(deviceName: DeviceName, sendData: BitConverter.ToString(data, startIndex, length).Replace("-", " "));

            _rwLock.EnterWriteLock();
            try
            {
                if (_serialPort?.IsOpen != true)
                    throw new InvalidOperationException("串口未连接");

                _serialPort.Write(data, startIndex, length);
                _rwLock?.ExitWriteLock();
            }
            catch (Exception)
            {
                _rwLock.ExitWriteLock();
                throw;
            }
        }

        #endregion 数据发送

        #region 数据接收

        /// <summary>
        /// 异步等待接收数据（带超时）
        /// </summary>
        protected byte[] Receive(Func<byte[], bool> receivedDataVerify, TimeSpan timeout, CancellationToken ct = default)
        {
            using var timeoutCts = new CancellationTokenSource(timeout);
            using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCts.Token);

            try
            {
                int bytesReadCount = 0;
                int bytesVerifyCount = 0;
                byte[] bytes = null!;
                while (true)
                {
                    while (bytesVerifyCount <= bytesReadCount)
                    {
                        if (bytesVerifyCount > 0)
                        {
                            byte[] dataVerifying = bytes.AsSpan(0, bytesVerifyCount).ToArray();
                            if (receivedDataVerify?.Invoke(dataVerifying) == true)
                            {
                                _rwLock.EnterReadLock();
                                try
                                {
                                    int remainCount = (int)_receiveBuffer.Length - bytesVerifyCount;
                                    if (remainCount > 0)
                                    {
                                        _receiveBuffer.Position = bytesVerifyCount;
                                        byte[] remainBytes = new byte[remainCount];
                                        _receiveBuffer.Read(remainBytes, 0, remainCount);
                                        _receiveBuffer = new MemoryStream();
                                        _receiveBuffer.Write(remainBytes, 0, remainCount);
                                        _receiveBufferSize = remainCount;
                                    }
                                    else
                                    {
                                        _receiveBuffer.SetLength(0);
                                        _receiveBufferSize = 0;
                                    }
                                    _rwLock.ExitReadLock();
                                }
                                catch (Exception)
                                {
                                    _rwLock.ExitReadLock();
                                    throw;
                                }

                                return bytes;
                            }
                        }
                        bytesVerifyCount++;
                    }

                    if (_receiveBufferSize > bytesReadCount)
                    {
                        bytesReadCount = (int)_receiveBufferSize;
                        _rwLock.EnterReadLock();

                        if (bytesReadCount > 0)
                            bytes = _receiveBuffer.ToArray();

                        _rwLock.ExitReadLock();
                    }

                    linkedCts.Token.ThrowIfCancellationRequested();
                    Thread.Sleep(20);
                }
            }
            catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
            {
                throw new TimeoutException("接收操作取消");
            }
        }

        private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            _rwLock.EnterWriteLock();
            try
            {
                {
                    if (_serialPort == null || !_serialPort.IsOpen) return;

                    int bytesToRead = _serialPort.BytesToRead;
                    if (bytesToRead == 0) return;

                    byte[] buffer = _arrayPool.Rent(bytesToRead);
                    try
                    {
                        int read = _serialPort.Read(buffer, 0, bytesToRead);
                        _receiveBuffer.Write(buffer, 0, read);
                        _receiveBufferSize = _receiveBuffer.Length;
                        MyLog.DeviceData(deviceName: DeviceName, receivedData: BitConverter.ToString(_receiveBuffer.ToArray()).Replace("-", " "));
                    }
                    finally
                    {
                        _arrayPool.Return(buffer);
                    }
                }
            }
            catch (Exception ex)
            {
                MyLog.Error(ex);
                // 记录错误日志
            }
            finally
            {
                _rwLock.ExitWriteLock();
            }
        }

        #endregion 数据接收

        #region 资源释放

        public void Dispose()
        {
            if (_disposed) return;

            _rwLock.EnterWriteLock();
            try
            {
                Disconnect();
                _serialPort?.Dispose();
                _receiveBuffer?.Dispose();
                _disposed = true;
            }
            catch (Exception ex)
            {
                MyLog.Error(ex);
            }
            finally
            {
                _rwLock.ExitWriteLock();
            }
        }

        #endregion 资源释放

        #region 辅助方法

        /// <summary>
        /// 清空接收缓冲区（线程安全）
        /// </summary>
        protected void ClearBuffer()
        {
            _rwLock.EnterWriteLock();
            try
            {
                _receiveBuffer.SetLength(0);
                _receiveBufferSize = 0;
                _serialPort?.DiscardInBuffer();
                _rwLock.ExitWriteLock();
            }
            catch (Exception)
            {
                _rwLock.ExitWriteLock();
                throw;
            }
        }

        #endregion 辅助方法
    }
}